#include "stdio.h"
#include "stdlib.h"
#include "string.h"
#include "shell.h"
#include "ctype.h"
#include "scheduler.h"
#include "command.h"
#include "console.h"
#include "kernel.h"
#include "history.h"
#include "libasm.h"

#define SHELL_MAX_ARGS 16
#define SHELL_DEFAULT_PROMPT ":> "

char *aliases[] = { "setdate", "clear", "help", "count",
       "ps", "kill", "test_args", "waitpid_test", "sleep_test", "idle",
       "shell", "setdate", "getdate", "echo", "clock", "killall",
		 "fibonacci", "fibonacci", "yes", "top", "fg", "shm_test", "reboot",
		 "malloc_test", "priority", "page_test", NULL };

programT programs[] = { setdate, clear, help, count,
	ps, kill, test_args, waitpid_test, sleep_test, idle,
	shell, setdate, getdate, echo, clock,
	killall, fibonacci, fibonacci_wrap, yes, top, fg, shm_test, reboot,
 	malloc_test, priority, page_test, NULL };

static char *read_shell_cmd(char buffer[SHELL_CMD_BUFFER_SIZE]);
static int make_argv(char *buffer, char *argv[SHELL_MAX_ARGS]);
static int is_background_program_call(int argc, char *argv[]);
static programT translate_program_alias(programT programs[], char *aliases[],
		char *alias);

int
shell(int shell_argc, char *shell_argv[])
{
	char buffer[SHELL_CMD_BUFFER_SIZE];
	char *argv[SHELL_MAX_ARGS + 1];
	char *prompt;
	int argc, is_bkg, pid;
	programT program;
	clear_screen(get_console_video(get_task_tty(0)));
	prompt = (shell_argc >= 2 ? shell_argv[1] : SHELL_DEFAULT_PROMPT);

	puts("TPE\n");
	puts("Trabajo Practico Especial: Multitasker\n\n\n");

	newHistory(get_active_tty_num());

	while (1)
	{
		puts(prompt);
		read_shell_cmd(buffer);
		addToHistory(get_active_tty_num(), buffer);
		argc = make_argv(buffer, argv);
		if (argv[0] == '\0')
			continue;
		is_bkg = is_background_program_call(argc, argv);
		program = translate_program_alias(programs, aliases, argv[0]);
		if (program != NULL)
		{
			/* Registrar el nuevo programa */
			if((pid = new_task(program, argc, argv, is_bkg)) == 0)
				puts("shell: no se puede cargar "
						"el proceso solicitado\n\n");
			else
				if ( !is_bkg )
					_waitpid(pid);
		}
		else
			puts("Comando incorrecto\n");
	}
	return 0;
}


static char *
read_shell_cmd(char buffer[SHELL_CMD_BUFFER_SIZE])
{
	unsigned int i;
	int c;

	for (i = 0; (c = getchar()) != EOF && c != '\n'; i++)
	{
		if (i < SHELL_CMD_BUFFER_SIZE - 1)
			buffer[i] =  (unsigned char) c;
	}
	if(c == EOF)
		putchar('\n');
	buffer[i] = '\0';

	return buffer;
}

static int
make_argv(char *buffer, char *argv[SHELL_MAX_ARGS])
{
	char *s;
	int eos;
	unsigned int i;

	s = buffer;
	eos = (*s == '\0');
	for (i = 0; i < SHELL_MAX_ARGS && !eos; i++)
	{
		s = ltrim(s);
		argv[i] = s;
		while (!(eos = (*s == '\0')) && !isspace(*s))
			s++;
		*s = '\0';
		s++;
	}
	argv[i] = NULL;

	return i;
}

static int
is_background_program_call(int argc, char *argv[])
{
	if (argc < 2)
		return 0;

	return (strcmp("&", argv[argc - 1]) == 0);
}

static programT
translate_program_alias(programT programs[], char *aliases[], char *alias)
{
	unsigned int i;

	for (i = 0; aliases[i]; i++)
		if (strcmp(aliases[i], alias) == 0)
			return programs[i];

	return NULL;
}

char *
ltrim(const char *s)
{
	/* no se usa isspace para evitar tener que compilar con otro archivo
	 * de implementacin de funciones de biblioteca estandar */
	while(*s == ' ' || *s == '\n' || *s == '\r' || *s == '\t'
			|| *s == '\f' || *s == '\v')
		s++;

	return (char *) s;
}


char *
get_command_alias(programT prg)
{
	unsigned int i;

	for (i = 0; aliases[i]; i++)
		if (prg == programs[i])
			return aliases[i];
	return "";
}
